<?

	// Fmgr0v0r01_dir is the base class for all directories.
	require_once '_common.php';
	
	class Fmgr0v0r01_dir extends Fmgr0v0r01__common implements Fmgr_Directory{

		var $media_class = 'Directory';
		
		function set_functions(){
			static $a;
			if(!$a) $a = array_merge(parent::set_functions(), array(
				'htaccess',
				'mode_recurse'
			));
			return $a;	
		}
		
		function get_functions(){
			static $a;
			if(!$a) $a = array_merge(parent::get_functions(), array(
				'contents',
				'children'
			));
			return $a;	
		}
		
		function critical_defaults(){
			return array_merge(parent::critical_defaults(), array(
				'filesize' => 0
			));	
		}
		
		protected $valid_child_classnames = '*';
		
 		protected $dir = 1;
		protected $mod = 755;

		protected $cdrn;
	
		protected $htrules;
		
		// Sticky check returns true for all clear, meaning no sticky or user is dir owner or root
		function sticky_check(){
			return (!$this->data->get('stb') || in_array($this->cxt, array('u', 'R')));	
		}
		
		function is_writex(){
			return ($this->allow_write && $this->allow_execute && $this->sticky_check());
		}
	
####	get functions
		
		function realpath($c=''){
			return parent::realpath().($c ? "/$c" : '');
		}
		
		function deskpath(){
			return parent::deskpath().'/';
		}
		
		function contents($flag=2){
			return (!$this->allow_read || (!$a = HostInterface::dir_contents($this->realpath(), $flag))) ? array() : $a;
		}
		
		function cdrn_regexp(){
			return "(^".addslashes($this->child_fmrp(''))."[^/]+$)";	
		}
		
		protected function cdrn($orphans=false){
			if(!is_null($this->cdrn)) return $this->cdrn;
			$this->cdrn = new jso;
			$cnt = $this->contents();
			if($qry = $this->pkg->select('*', array('?fmrp'=>$this->cdrn_regexp(), 'xst'=>1), '`dir` DESC, `fmrp` ASC')){
				if(!is_array($qry)) $qry = array($qry);
				foreach($qry as $child){
					$b = $child->basename();
					if(!isset($cnt[$b])){
						$orphans ? $this->cdrn->set($b, $child) : $child->commit();
						continue;
					}
					$this->cdrn->set($b, $child);
					unset($cnt[$b]);
				}
			}
			foreach($cnt as $b) $this->appendChild($this->realpath($b));
			return $this->cdrn;
		}
		
		function children(){
			if(!$this->allow_read) return new obj;
			return $this->cdrn();
		}
		
		function length(){
			if($this->cdrn) return $this->cdrn->length();
			return count($this->contents());
		}
		
		// child() is commonly accessed by the package and potentially other scripts
		function child($b){
			if(!$this->allow_read || !$b || !file_exists($fp = $this->realpath($b))) return false;
			if($child = $this->pkg->open($this->child_fmrp($b))) return $child;
			return $this->appendChild($fp);
		}
		
		function child_fmrp($c=''){
			return $this->data->fmrp."/$c";	
		}
		
		protected function read_filesize(){
			$qs = "SELECT SUM(filesize) FROM `".$this->table()."` WHERE `fmrp` REGEXP '".$this->cdrn_regexp()."' AND `xst` = 1;";
			if(!$r = $this->pkg->al()->execute($qs)) return;
			if(!$fs = $r[0]['SUM(filesize)']) $fs = 0;
			return $this->edits['filesize'] = $fs;
		}
		
		function htaccess(){
			return ($h = $this->data->get('tmpl'))  ? ((($ht = $this->db()->get($h))&&($ht->class == 'htaccess')) ? $ht : false) : false;	
		}
		
		function htrules(){
			if(is_null($this->htrules)){
				$this->htrules = new htrules;
				if(!$this->pkg->test_instanceof($this, '_rootdir')) $this->htrules->set($this->parent()->htrules());
				if($ht = $this->htaccess()) $this->htrules->set($ht->rules());
			}
			return $this->htrules;
		}
		
		function permit_child_basename($b){
			return basename(HostInterface::filename_increment($this->realpath($b), array($this, 'file_exists')));
		}
		
		function join_index($index){
			if(!$this->allow_read) return;
			$index->set($this->id, $this->href());
			foreach($this->children() as $child) $child->join_index($index);
		}
		
####	set functions
		
		function file_exists($fp){
			if(file_exists($fp)) return true;
			$al = $this->pkg->al();
			$tb = $this->table();
			$where = array('fmrp'=>$this->child_fmrp(basename($fp)));
			if($d = $al->select(array('xst','updated'), $tb, $where)){
				$t = time();
				return ($d[0]['xst']) ? ($al->update($tb, array('xst'=>0, 'updated'=>$t), $where) || true) : 
						($d[0]['updated']+2419200 < $t) ? (!$al->delete($tb, $where)) : true;
			}
			return false;
		}
		
		function build_basename(){
			$b = ($b = $this->get_intended('basename')) ? $b : $this->basename();
			return smuoi_dirname($b);
		}
			
		protected function update_fmrp($fmrp){
			$ofmrp = $this->data->fmrp;
			$this->do_update_fmrp($fmrp);
			$al = $this->pkg->al();
			$tb = $this->table();
			if(!$a = $al->select('fmrp', $tb, array('%fmrp'=>"$ofmrp/%"))) return;
			foreach($a as $c){
				$n['fmrp'] = $fmrp.substr($c['fmrp'], strlen($ofmrp));
				if($rsrc = $this->pkg->get_cached($c['fmrp'])) $rsrc->do_update_fmrp($n['fmrp']);
				$al->update($tb, $n, $c);
			}
		}
		
		// set mode recurse is called at the end of the set function list
		protected function set_mode_recurse($r){
			if(!$r || !is_root()) return false;
			foreach(array('own','grp','mod') as $p) $set[$p] = $this->data->get($p);
			$where = array('%fmrp'=>$this->child_fmrp("%"), 'dir'=>1);
			$tb = $this->table();
			$this->pkg->al()->update($tb, $set, $where);
			$set['mod'] = $this->mk_file_mode();
			$where['dir'] = 0;
			$this->pkg->al()->update($tb, $set, $where);
		}
			
		function copy_to(Fmgr_Directory $par){
			if(!$this->allow_read || !$par->is_writex()) return false;
			if(!$new = $par->mkdir($this->basename(), false, 1)) return false;
			foreach($this->children() as $child) $child->copy_to($new);
			return $new;
		}
		
		protected function set_htaccess($h){
			if(!is_root()) return false;
			if($h && (!$this->pkg->test_instanceof($h, 'htaccess'))) return false;
			$this->hard_set('tmpl', (($h) ? $h->id : ''));
			return true;
		}
		
####	commital


####	Adding and removing children

		// $o may be:
		//	string full path to new file
		//	object existing fmgr rsrc
		//	object containing new child data
		function appendChild($o){
			if((!$o)||(!$this->is_writex()))  return false;
			// String? must be full path.
			if(is_string($o) && (substr($o, 0, 1) == '/')){
				if(!$o = $this->pkg->build_file($o)) return false;
			}
			// If we're here $o must be an object, and must have a class
			if((!is_object($o)) || !$this->is_valid_child($o)) return false;
			
			// If it's already a resource just commit the par change and return, we're done.
			if(smuoi_is_rsrc($o)){
				if($o->is_local() != $this->is_local()) return $o->copy_to($this);
				$o->parent = $this;
				$o->commit();
				if(!is_null($this->cdrn)) $this->cdrn->set($o->basename(), $o);
				return $o;
			}
			// If we're here, it's a new file.
			if(!$basename = $this->take_coldfile($o->srcpath)) return false;
			// So now we start passing on inherent values
			$o->par = $this->id;
			// Our path and rdk
			$o->fmrp = $this->child_fmrp($basename);
			// Belongs to current user
			if(!isset($o->own) || !is_root()) $o->own = usr()->id;
			// Sticky GUID is hard-coded into smuoi directories.  To change the group of new files you must do so after creation.
			if(!isset($o->grp) || !is_root()) $o->grp = $this->data->grp;	
			// Pass on mode to directories
			$o->mod  = ($o->dir) ? $this->mode() : $this->mk_file_mode();
			require_once $this->pkg->path_to_dataclass($o->class);
			$class = $this->pkg->apply_namespace($o->class);
			$child = new $class(false, $this->pkg, $o);
			$child->parobj = $this;
			$child->commit(1);
			if($this->cdrn) $this->cdrn->set($basename, $child);
			$this->pkg->cache($child);
			return $child;
		}
		
		// for file remove the executable bit (if present) from each bit of our own mode
		function mk_file_mode(){
			$mod = '';
			foreach($this->split_mode() as $p) $mod .= ($p &1) ? $p-1 : $p;	
			return $mod;
		}
		
		// take_coldfile takes a *non-registered* file, checks the name,
		// and if necessary moves it here.  Returns (possibly altered) basename of file.
		protected function take_coldfile($src){
			if(!file_exists($src)) return false;
			$basename = (is_dir($src)) ? smuoi_dirname(basename($src)) : smuoi_filename(basename($src));
			$dst = $this->realpath($basename);
			if(file_exists($dst)){
				if($src == $dst) return $basename;
				$basename = basename($dst = filename_increment($dst));
			}
			return (rename($src, $dst)) ? $basename : false;
		}
		
		function mkdir($name, $set=false){
			if(!$this->is_writex()) return false;
			if(!$name = smuoi_dirname($name)) return false;
			$fp = filename_increment($this->realpath($name));
			mkdir($fp, 0755);
			return $this->appendChild($this->pkg->build_file($fp, $set));
		}
		
		function mkfil($name, $set=false){
			if(!$this->is_writex()) return false;
			if(!$name = smuoi_filename($name)) return false;
			$fp = filename_increment($this->realpath($name));
			$string = (is_array($set) && isset($set['contents'])) ? $set['contents'] : '';
			writeToFile($fp, $string);
			chmod($fp, 0644);
			return $this->appendChild($this->pkg->build_file($fp, $set));
		}
		
		function is_valid_child($o){
			if(!isset($o->class)) return false;
			return ($this->is_valid_child_class($o->class));
		}
		
		function is_valid_child_class($c){
			return ($this->in_settings_array($c, 'valid_child_classnames', 1) && $this->pkg->is_valid_dataclass($c));
		}

		protected function removeChild($child, $b){
			$this->commit_on_shutdown();
			if($this->cdrn) $this->cdrn->remove($b);
		}
		
		function commit_on_shutdown(){
			parent::commit_on_shutdown();
			$this->add_sanity_check('filesize');
		}
		
		protected function check($c){
			switch($c){
				case 'filesize':
					return $this->read_filesize();
				case 'class':
					foreach($this->pkg->read_coldfile($this->realpath()) as $k=>$v){
						$this->edits[$k] = $v;
						unset($this->sanity_checks[$k]);
					}
			}
		}

		function isMyChild(Smuoi_Resource $child){
			return ($child->parent() === $this);
		}

		function destroy(){
			if(!$this->is_destroyable()) return false;
			$cdrn = $this->cdrn(1);
			foreach($cdrn as $child) if(!$child->is_destroyable()) return false;
			foreach($cdrn as $child) if(!$child->destroy()) return false;
			if(count($this->contents())) return false;
			if(is_dir($this->realpath())) rmdir($this->realpath());
			return $this->destroy_row();
		}
	}
	
?>